package com.pic.mgt.haishin.action;

import java.io.File;
import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.quartz.Trigger;
import org.springframework.dao.DataAccessException;

import com.pic.mgt.IgxStatus;
import com.pic.mgt.action.AbstractMissUnitInspectActionImpl;
import com.pic.mgt.action.BusinessRecoverAction;
import com.pic.mgt.dao.haishin.HaishinBusinessMgtInfoBean;
import com.pic.mgt.dao.haishin.IHaishinBusinessMgtDao;
import com.pic.mgt.haishin.HaishinScheduler;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.monitor.WrongUnitFormatException;
import com.pic.mgt.monitor.parser.FormatParser;
import com.pic.mgt.scheduler.SchedulerException;

public class HaishinMissBusinessUnitInspectActionImpl extends
		AbstractMissUnitInspectActionImpl {
	
	private BusinessRecoverAction haishinBusinessRecoverAction;
	
	private IHaishinBusinessMgtDao haishinBusinessMgtDao;
	
	private FormatParser businessNameParser;
	
	private String haishinInspectBizInboxFolderStr;
	private long haishinInspectThreshold;
	private String bizConfirmationFilenameSuffix;
	
	private HaishinScheduler haishinScheduler;
	
	@Override
	public synchronized boolean inspectMissUnitAndRecover() {
		// ********************** Log **********************
		IgxLog.messageLog.info(this, "Starting miss business inspection process.");
		// ********************** Log **********************
		
		// 取得現在時間
		Date nowDate = new Date();
		
		
		// (1) 檢測 haishin inbox 目錄下的業務確認檔,是否有存在超過指定的時間
		SuffixFilenameFilter bizConfirmationFilenameFilter = new SuffixFilenameFilter(this.bizConfirmationFilenameSuffix);
		File[] missBusinessConfirmationFileArray =
			super.checkFileMissUnderFolder(new File(this.haishinInspectBizInboxFolderStr),
										   nowDate,
										   this.haishinInspectThreshold,
										   bizConfirmationFilenameFilter);
		if (null != missBusinessConfirmationFileArray) {
			for (File missBusinessConfirmationFile : missBusinessConfirmationFileArray) {
				long missBizConfirmationFilesize = missBusinessConfirmationFile.length();
				
				// 如果開啟忽略 0KB 檔案的功能,則要做檢查
				if (super.isIgnoreEmptyBizConfirmationFile) {
					if (missBizConfirmationFilesize <= 0) {
						continue; // 表0KB的確認檔,則忽略之
					}
				}
				
				/*
				 * 如果確認檔的大小大於 0KB,則再做一次判斷這個確認檔是不是已經超過指定的時間。
				 * 
				 * 要這麼做的原因是因為，如果檔案送過來時失敗了(有檔但大小為0KB)，這時通常送端自己
				 * 會有 Retry 機制，可能會重傳。而在這裡做如果大於 0KB 的話則要再確認一次的原因，
				 * 就是要避免可能在此時，送端正在重傳了(所以大小不會為0KB了，同時檔案的時間也會
				 * 被更新)，會導致這次的重傳完畢了被系統偵測到，然後處理掉，這時如果再 recovery
				 * 就會有問題。
				 */
				if (missBizConfirmationFilesize > 0) {
					boolean isFileExistedOverTime =
						super.isFileExistedOverTime(missBusinessConfirmationFile,
													nowDate,
													this.haishinInspectThreshold);
					if (!isFileExistedOverTime) {
						continue;
					}
				}
				
				// --------- 確認檔案存在超過指定時間,以下開始做判斷和處理 --------------
				
				/* 配信的業務雖然超過指定的時間還停留在 Haishin INBOX 底下，
				 * 但是因為配信的業務是可以指定未來時間作配信，所以會留在 Haishin INBOX
				 * 底下直到指定的時間到了才會被處理，所以留在 Haishin INBOX 底下是正常的。
				 * 因此，如果有配信業務超過指定的時間還停在 Haishin INBOX底下，不能就視為
				 * 被 Miss 的業務檔，要再去做判斷才能確定。
				 */
				
				// 先取得 target file name
				String targetFilename = StringUtils.removeEnd(
											missBusinessConfirmationFile.getName(),
											this.bizConfirmationFilenameSuffix);
				
				// 從資料庫中取得該筆業務的記錄
				HaishinBusinessMgtInfoBean haishinBusinessMgtInfoBean;
				try {
					haishinBusinessMgtInfoBean =
							this.haishinBusinessMgtDao.findHaishinBusinessMgtInfoByUnitName(targetFilename);
				} catch (DataAccessException e) {
					// ********************** Log **********************
					IgxLog.messageDetailsLog.error(this, e.getMessage());
					// ********************** Log **********************
					
					// ********************** Log **********************
					IgxLog.messageLog.error(this,
									"SUDA-329 Miss business inspection　process failed due to database error.");
					// ********************** Log **********************
					
					return false;
				}
				
				if (null == haishinBusinessMgtInfoBean) {
					// 如果資料庫中根本還沒有組入的記錄,則表示需要 Recover
					
					// ********************** Log **********************
					IgxLog.messageLog.info(this,
							"Miss Business Inspection: detected a miss business '{}'.",
							missBusinessConfirmationFile.getAbsolutePath());
					// ********************** Log **********************
					
					this.haishinBusinessRecoverAction.recoverBusiness(targetFilename);
				}
				else {
					// 如果資料庫已有組入的記錄
					
					try {
						// 取得 business name
						// throws WrongUnitFormatException
						String businessName = (String)this.businessNameParser.parse(missBusinessConfirmationFile.getName());
						// 取得 Job 的 Schedule 狀態
						// throws SchedulerException
						 int lastJobStat = this.haishinScheduler.getJobStatus(targetFilename, businessName);
						 
						 if (lastJobStat == Trigger.STATE_NONE) { // 表示沒有該 Job 排程 
							 /*
							  * 沒有 Job 排程，有以下三種情況：
							  * (1) 根本就沒還沒有加入排程中
							  * (2) 已加入排程，且執行時間已到並已執行完,執行完後就不會存在排程中了
							  * (3) 已加入排程，且執行時間已到，但在執行中系統 crash 掉，這時 Job 排程的狀態應會停留在
							  *     Trigger.STATE_COMPLETE，但下次系統再起來把 Quartz 載入來，Quartz 會自己檢查，把
							  *     該 JOB 視為已執行完，將這 Job 排程移除掉。
							  */
							 
							 /* 
							  * 在確定沒有該排程的情況下,再去取得該業務一次. (主要是要
							  * 確保拿到最後.最新的業務狀態)
							  */
							 try {
									haishinBusinessMgtInfoBean =
											this.haishinBusinessMgtDao.findHaishinBusinessMgtInfoByUnitName(targetFilename);
								} catch (DataAccessException e) {
									// ********************** Log **********************
									IgxLog.messageDetailsLog.error(this, e.getMessage());
									// ********************** Log **********************
									
									// ********************** Log **********************
									IgxLog.messageLog.error(this,
													"SUDA-329 Miss business inspection　process failed due to database error.");
									// ********************** Log **********************
									
									return false;
								}
							 
							 /*
							  *  判斷是不是 Miss 的條件:
							  *  只要業務的狀態不是 BUSINESS_COMPLETED 都表示為 Miss 的業務.
							  *  (BUSINESS_FAILED除外,FAILED 狀態表示有執行完成,只是因為某
							  *  些原因有 fail 掉,不能視為 miss 的業務)
							  */
							 if (!haishinBusinessMgtInfoBean.getStatus().equals(IgxStatus.BUSINESS_COMPLETED) &&
								 !haishinBusinessMgtInfoBean.getStatus().equals(IgxStatus.BUSINESS_FAILED)) {
								 /*
								  * 若 已經沒有 Job 排程了,狀態卻停留在這幾種 (BUSINESS_REDO, BUSINESS_LOADED, BUSINESS_SCHEDULED,
								  * BUSINESS_EXECUTING, BUSINESS_EXECUTING_FINISH),表示是上次執行到一半不明原因 crash掉,是為 miss 的
								  * 業務. 
								  */
								 
								 // ********************** Log **********************
								 IgxLog.messageLog.info(this,
										 "Miss Business Inspection: detected a miss business '{}'.",
										 missBusinessConfirmationFile.getAbsolutePath());
								 // ********************** Log **********************

								 this.haishinBusinessRecoverAction.recoverBusiness(targetFilename);
							 }
							 else {
								 /*
								  * 表示業務已經做完:
								  * 若為 BUSINESS_COMPLETED: 表示在檢核當下,該業務也剛好被執行完成了.
								  * 若為 BUSINESS_FAILED: 表示之前的執行已經完成,只是因為某些原因失敗了,所以不算是 miss 的業務.
								  */
							 }
						 }
						 else {
							 /*
							  * Job 其它的狀態有：
							  * Trigger.STATE_BLOCKED: 表示 Job 已經要執行，但因 Thread 不夠，在等待 Thread.
							  * Trigger.STATE_COMPLETE: 表示 Job 正在執行當中，或執行完畢
							  * Trigger.STATE_ERROR: 表示該 Job 的排程上有錯誤
							  * Trigger.STATE_NORMAL: 表示該 Job 執行時間尚未到達，還在等待當中
							  * Trigger.STATE_PAUSED: 表示該 Job 排程暫停中
							  * 以上的情況，都表示 Job 都還存在或在執行當中，所以不是 miss 的業務。
							  */
						 }
					} catch (WrongUnitFormatException e) {
						// ********************** Log **********************
						IgxLog.messageDetailsLog.error(this, e.getMessage());
						// ********************** Log **********************
						
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-330 Miss Business Inspection: miss business '{}' inspection failed. Because of wrong format.",
										targetFilename);
						// ********************** Log **********************
						
						continue;
					} catch (SchedulerException e) {
						// ********************** Log **********************
						IgxLog.messageLog.error(this,
										"SUDA-331 Miss Business Inspection: miss business '{}' inspection failed. Because of getting job schedule info error.",
										targetFilename);
						// ********************** Log **********************
						
						continue;
					}
				}
			}
		}
		
		// ********************** Log **********************
		IgxLog.messageLog.info(this, "Miss business inspection process completed.");
		// ********************** Log **********************
		
		return true;
	}
	
	// --- getter and setter ---

	public void setHaishinBusinessRecoverAction(
			BusinessRecoverAction haishinBusinessRecoverAction) {
		this.haishinBusinessRecoverAction = haishinBusinessRecoverAction;
	}

	public void setHaishinInspectBizInboxFolderStr(
			String haishinInspectBizInboxFolderStr) {
		this.haishinInspectBizInboxFolderStr = haishinInspectBizInboxFolderStr;
	}

	public void setHaishinInspectThreshold(long haishinInspectThreshold) {
		this.haishinInspectThreshold = haishinInspectThreshold;
	}

	public void setBizConfirmationFilenameSuffix(
			String bizConfirmationFilenameSuffix) {
		this.bizConfirmationFilenameSuffix = bizConfirmationFilenameSuffix;
	}

	public void setHaishinScheduler(HaishinScheduler haishinScheduler) {
		this.haishinScheduler = haishinScheduler;
	}

	public void setHaishinBusinessMgtDao(
			IHaishinBusinessMgtDao haishinBusinessMgtDao) {
		this.haishinBusinessMgtDao = haishinBusinessMgtDao;
	}

	public void setBusinessNameParser(FormatParser businessNameParser) {
		this.businessNameParser = businessNameParser;
	}

}
